<?php

declare(strict_types=1);

namespace App\Middleware;


use App\Constants\Business;
use App\Log;
use App\Utils\Helper;
use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
use Hyperf\Context\Context;
use Hyperf\Validation\ValidationException;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use L1n6yun\HyperfJwt\Exceptions\AuthException;
use L1n6yun\HyperfJwt\Exceptions\UnauthorizedException;
use function L1n6yun\HyperfJwt\auth;

class ApiRequestMiddleware implements MiddlewareInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var HttpResponse
     */
    protected $response;


    public function __construct(ContainerInterface $container, HttpResponse $response, RequestInterface $request)
    {
        $this->container = $container;
        $this->response = $response;
        $this->request = $request;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 忽略swagger组件
        if (!isProd() && stripos($request->getUri()->getPath(), 'swagger')) {
            return $handler->handle($request);
        }

        // 处理正常请求
        $start_logic = Helper::getMillisecond();
        $requestData['headers'] = $request->getHeaders();
        $requestData['method'] = $request->getMethod();
        $requestData['url'] = $request->getUri()->getPath();
        $requestData['request'] = array_merge($request->getParsedBody(), $request->getQueryParams());

        try {
            $handleData = $handler->handle($request);
            $requestData['userInfo'] = $this->getTokenUser();
            $requestData['response'] = json_decode($handleData->getBody()->getContents(), true);
            $requestData['handle_time'] = Helper::getMillisecond() - $start_logic;
            Log::custom('ApiRequest', $requestData);
            return $handleData;
        } catch (BusinessException $e) {
            $requestData['userInfo'] = $this->getTokenUser();
            $requestData['error'] = $this->getErrorRow($e);
            $requestData['handle_time'] = Helper::getMillisecond() - $start_logic;
            Log::custom('ApiBusinessTip', $requestData);
            return Helper::respondError($e->getCode(), $e->getMessage(), $e->getData());
        } catch (AuthException | UnauthorizedException $e) {
            $requestData['userInfo'] = $this->getTokenUser();
            $requestData['error'] = [
                'code' => ErrorCode::USER_FORBIDDEN,
                'msg' => ErrorCode::getMessage(ErrorCode::USER_FORBIDDEN),
                'origin' => $this->getErrorRow($e)
            ];
            Log::custom('JWTAuthError', $requestData);
            return Helper::respondError(ErrorCode::USER_FORBIDDEN, ErrorCode::getMessage(ErrorCode::USER_FORBIDDEN));
        } catch (ValidationException $e) {
            $requestData['userInfo'] = $this->getTokenUser();
            $requestData['error'] = [
                'code' => ErrorCode::PARAMETERS_ERROR,
                'msg' => ErrorCode::getMessage(ErrorCode::PARAMETERS_ERROR),
                'origin' => $e->errors()
            ];
            Log::custom('ValidationError', $requestData);
            return Helper::respondError(ErrorCode::PARAMETERS_ERROR, ErrorCode::getMessage(ErrorCode::PARAMETERS_ERROR), $e->errors());
        } catch (\Throwable $e) {
            $requestData['userInfo'] = $this->getTokenUser();
            $requestData['error'] = $this->getErrorRow($e);
            Log::custom('ApiRequestError', $requestData);
            return Helper::respondError(ErrorCode::SERVER_ERROR, sprintf("%s[%s]:%s", $e->getFile(), $e->getLine(), $e->getMessage()));
        }
    }

    /**
     * Notes: 拼接错误信息数组
     * User: Ken
     * DateTime: 2024/10/16
     * @param object $e
     * @param array
     */
    private function getErrorRow(object $e): array
    {
        return [
            'code' => $e->getCode(),
            'line' => $e->getLine(),
            'file' => $e->getFile(),
            'tip' => $e->getMessage(),
        ];
    }

    /**
     * Notes: 根据Token, 获取用户信息
     * User: Ken
     * DateTime: 2024/10/16
     * @param array
     */
    private function getTokenUser(): array
    {
        // 在Controller层，使用注解进行权限验证 (L1n6yun\HyperfJwt\Annotation\Auth（#[Auth]注解适用于类和方法）)
        return Context::get('auth.user') ? auth()->user()->getUserRow() : [];
    }
}
